home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 22
/
Cream of the Crop 22.iso
/
bbs
/
pprd199.zip
/
PPRD.C
< prev
next >
Wrap
C/C++ Source or Header
|
1996-10-06
|
25KB
|
1,027 lines
/*
PPRD
Line printer daemon using Berkeley LPD protocol on top of WATTCP for up
to 3 parallel printer ports.
An alternate protocol called JD is supported also. In this the daemon
waits on port 9100 and copies data to the printer until EOF.
This daemon is written as a state machine because it has to support
several connections on a single threaded system, DOS.
When started up, it checks to see how many printers are known to the
BIOS. These printers are served as LPT[123]. It listens on the
standard LPD port 515, but this can be changed from the command line.
Examples of options:
-p1515 listen on port 1515 instead
-j9200 use direct protocol instead at port 9200 (defaults to 9100 if unspec)
-23 disable printers 2 and 3
-n2 two printers, no matter what BIOS claims
-b12 on 1 and 2 bypass BIOS and send directly to port (parallel ports only)
-t don't indicate available printers with tones
-i reinitialise printer via hardware line on job abort
-k abort job if client drops connection
-s disable subnet match (server and client must be on same subnet)
-alist comma separated list of up to 20 hosts/networks allowed connection
-dlist comma separated list of up to 20 hosts/networks denied connection
last two are mutually exclusive
-lhost log diagnostics to host of that domain name
-c output to console (for debugging only)
-g display memory buffer status graphically
-m measure transfer rate, don't print
Jobs can be aborted by C-F1 through C-F3 for that printer.
The LPD protocol is summarised below where S = server and C = client.
Ack = '\0' and nak = '\001'.
C: \002printer\n
S: ack/nak
C: \003size datafilename\n (size is bytes as decimal string)
S: ack/nak
C: size bytes of data + one '\0' byte
S: ack/nak
C: \002size cntlfilename\n
S: ack/nak
C: size bytes of control + one '\0' byte
S: ack/nak
The data and control files can occur in either order. In this
implementation the filenames are ignored.
If the control file is sent first, then a zero length data file indicates
that EOF == EOJ. This is a RFC1179 extension to existing lpds.
This program was compiled under Borland C++ 3.1 in C mode for the compact
model. You will require the WATTCP libraries. A copy is included in the
distribution. For the sources of WATTCP, ask archie where the archives
are.
Please send bug fixes, ports and enhancements to the author for
incorporation in newer versions.
Copyright (C) 1996 Ken Yap (ken@syd.dit.csiro.au)
This program is free software; you can redistribute it and/or modify it
under the terms of the Artistic License, a copy of which is included
with this distribution.
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <sys/types.h>
#include <time.h>
#include <tcp.h>
#include <alloc.h>
#include <dos.h>
#include <bios.h>
#include <conio.h>
#include "pprd.h"
#define PROGRAM "PPRD"
#define VERSION "Version 1.99 15 Oct 1996"
#define AUTHOR "Copyright (C) 1996 Ken Yap (ken@syd.dit.csiro.au)"
#define MAXLPT 3 /* supported under DOS */
#define MAXCON (MAXLPT+1) /* +1 for lpq, and direct */
/* try this many times to see if can accept next byte right away */
#define POLL_MAX 10
#define MDEBUG /* enable code to debug memory usage */
unsigned int lpdport = LPDPORT;
unsigned int jdport = JDPORT;
int jdprotocol = 0; /* use direct protocol if != 0 */
int nlpt = 0;
struct lpt_info lpt[MAXLPT];
int notone = 0;
int reinit = 0; /* reinitialise on abort */
int killjob = 1; /* abort on connection job */
unsigned int lpt_tone[MAXLPT] = { 523, 587, 659 }; /* CDE */
char lpt_names[MAXLPT][16] = { "lpt1", "lpt2", "lpt3" };
struct conn_info conn[MAXCON]; /* one extra for lpq/lprm requests */
struct conn_info log; /* for connection to syslogd */
unsigned int buffer_size; /* number of bytes per conn buffer */
int toconsole = 0; /* write to console for debugging */
int graphmem = 0; /* display memory usage graph */
int measureonly = 0;/* measure transfer rate only */
char *loghost = 0;
int check_subnet = 1;
int nallow = 0, ndeny = 0;
longword allow[20], deny[20];
void (*normal_init)(char *name, char *value);
#define MAXALLOW (sizeof(allow)/sizeof(allow[0]))
#define MAXDENY (sizeof(deny)/sizeof(deny[0]))
#define is_net_addr(a) (((a) & ~sin_mask) == 0)
#define is_on_net(a,n) (((a) & sin_mask) == (n))
extern longword my_ip_addr;
extern longword sin_mask;
#define ack_cmd(c) (void)sock_fastwrite(&c->sock, (byte *)"", 1)
#define nak_cmd(c) (void)sock_fastwrite(&c->sock, (byte *)"\001", 1)
char *ptime(void)
{
time_t t;
char *p;
t = time(0);
p = ctime(&t);
p[24] = '\0';
return (p);
}
void init_queues(void)
{
struct conn_info *c;
int i, ret;
struct lpt_info *p;
if (nlpt == 0) /* not overridden by -n? */
{
ret = biosequip();
nlpt = (ret >> 14) & 0x3;
}
for (i = 0; i < nlpt; ++i)
{
p = &lpt[i];
if (p->avail == DISABLED) /* unavail from cmd line? */
continue;
(void)bios_printer_init(i); /* initialise printer */
sleep(2); /* let printer settle */
p->status = bios_printer_status(i); /* get status */
if (p->status & (P_TIMEOUT|P_IOERROR))
{
p->avail = NONESUCH; /* printer not available */
(void)printf("%s: Error initialising %s\n",
ptime(), lpt_names[i]);
}
else
{
p->avail = FREE; /* printer available */
(void)printf("%s: %s initialised",
ptime(), lpt_names[i]);
if (p->hwaddr && (ret = peek(0, 0x408 + i * 2)) != 0)
(void)printf(", direct hardware access at %#3X",
p->hwaddr = ret);
(void)printf("\n");
}
}
for (i = 0; i < MAXLPT; ++i)
{
if (notone || lpt[i].avail != FREE)
continue;
sound(lpt_tone[i]);
sleep(1);
nosound();
}
buffer_size = farcoreleft() / 1024L; /* work in units of kb */
printf("%s: %uk free memory, ", ptime(), buffer_size);
buffer_size -= 2U; /* for log buffer */
buffer_size /= MAXCON; /* divide it up fairly */
if (buffer_size > BUFFER_SIZE)
buffer_size = BUFFER_SIZE;
printf("%uk per buffer\n", buffer_size);
buffer_size *= 1024U; /* now work in bytes */
log.buffer = farmalloc(2048UL); /* log buffer is fixed length */
/* should error check above but farmalloc below will bomb out anyway if
we are out of memory */
for (i = 0; i < MAXCON; ++i)
{
c = &conn[i];
c->state = INIT; /* initial connection state */
c->controlfirst = 0;
c->printer = -1;
if ((c->buffer = farmalloc((unsigned long)buffer_size)) == 0)
{
printf("Out of heap memory\n");
exit(1);
}
}
}
void init_log(void)
{
longword logip;
if (loghost == 0 ||
(logip = resolve(loghost)) == (longword)0 ||
udp_open(&log.sock, 0, logip, LOGPORT, NULL) == 0)
{
loghost = 0;
return;
}
(void)sprintf(log.buffer, LOG_TAG PROGRAM " " VERSION
", %d printer(s)\n", nlpt);
(void)sock_fastwrite(&log.sock, log.buffer, strlen(log.buffer));
}
void report_change(char *msg, int printer)
{
(void)printf("%s: %s %s\n", ptime(), lpt_names[printer], msg);
if (loghost != 0) /* log to syslogd */
{
(void)sprintf(log.buffer, LOG_TAG "%s %s\n", lpt_names[printer], msg);
(void)sock_fastwrite(&log.sock, log.buffer, strlen(log.buffer));
}
}
void lpt_status_change(int i, int delta, int status)
{
if (status & P_TIMEOUT)
report_change("time out", i);
if (status & P_IOERROR)
report_change("I/O error", i);
if (delta & P_SELECTED)
{
if (status & P_SELECTED)
report_change("online", i);
else
report_change("offline", i);
}
if (status & P_NOPAPER)
report_change("paper out", i);
}
int same_subnet(longword client)
{
/* assumes that bit operations can be done on longword */
return (((client ^ my_ip_addr) & sin_mask) == 0);
}
int in_list(longword ip, longword table[], int entries, int maxtable, int answer)
{
int i;
for (i = 0; i < entries && i < maxtable; ++i)
if ((is_net_addr(table[i]) && is_on_net(ip, table[i]))
|| ip == table[i])
return (answer);
return (!answer);
}
int check_access(longword ip)
{
if (check_subnet && same_subnet(ip))
return (1);
if (nallow > 0)
return (in_list(ip, allow, nallow, MAXALLOW, 1));
if (ndeny > 0)
return (in_list(ip, deny, ndeny, MAXDENY, 0));
/* otherwise denied */
return (0);
}
/*
* Translate from name to printer number
*/
int printernumber(char *printername)
{
int i;
for (i = 0; i < MAXLPT; ++i)
if (stricmp(lpt_names[i], printername) == 0)
return (i);
return (-1);
}
void queuename(struct conn_info *c)
{
int i, current, delta;
char printername[sizeof(lpt_names[0])];
*c->bufip = '\0';
if (*c->buffer != '\002' && *c->buffer != '\003')
{
c->state = NAKANDCLOSE;
return;
}
if (sscanf(c->buffer+1, "%15s", printername) != 1 ||
(i = printernumber(printername)) < 0)
{
(void)printf("%s: Printer name error: %s", ptime(), c->buffer+1);
c->state = NAKANDCLOSE;
return;
}
switch (*c->buffer)
{
case '\002':
if (lpt[i].avail != FREE)
{
(void)printf("%s: Printer not available: %s",
ptime(), c->buffer+1);
c->state = NAKANDCLOSE;
return;
}
c->printer = i;
lpt[i].avail = BUSY;
(void)printf("%s: Job for %s started\n",
ptime(), lpt_names[i]);
c->state = RECVJOB;
ack_cmd(c);
break;
case '\003':
case '\004':
(void)sprintf(c->buffer, "%s is ", lpt_names[i]);
switch (lpt[i].avail)
{
case BUSY:
(void)strcat(c->buffer, "busy\n");
break;
case FREE:
(void)strcat(c->buffer, "available");
/* get status of printer and save it */
lpt[i].status = current = bios_printer_status(i);
(void)strcat(c->buffer, current & P_SELECTED ?
" online" : " offline");
if (current & P_NOPAPER)
(void)strcat(c->buffer, " no paper");
(void)strcat(c->buffer, "\n");
break;
default:
(void)strcat(c->buffer, "not available\n");
break;
}
(void)sock_fastwrite(&c->sock, c->buffer, strlen(c->buffer));
c->state = CLOSING;
break;
}
}
void get_jobinfo(struct conn_info *c)
{
char *p;
p = c->bufip;
if (buffer_room(c) == 0)
--p;
*p = '\0';
for (p = strtok(c->buffer, "\n"); p != 0; p = strtok(0, "\n"))
{
switch (*p)
{
case 'N':
(void)strncat(c->jobname, p+1, sizeof(c->jobname)-1);
break;
case 'P':
(void)strncat(c->username, p+1, sizeof(c->username)-1);
break;
case 'H':
(void)strncat(c->hostname, p+1, sizeof(c->hostname)-1);
break;
}
}
}
#ifdef MDEBUG
/*
* Print graphical trace of buffer utilisation.
* 63 characters: X or x is used block, - is free.
* Small bug: a full buffer displays like an empty one.
* Not worth fixing because this is for debugging anyway.
*/
void trace_queue(struct conn_info *c)
{
int i;
unsigned int ioff, ooff;
ioff = c->bufip - c->buffer;
ooff = c->bufop - c->buffer;
ioff /= (buffer_size / 64);
ooff /= (buffer_size / 64);
if (ioff < ooff)
{
for (i = 0; i < ioff; ++i)
putchar('X');
for ( ; i < ooff; ++i)
putchar('-');
for ( ; i < 63; ++i)
putchar('x');
}
else
{
for (i = 0; i < ooff; ++i)
putchar('-');
for ( ; i < ioff; ++i)
putchar('X');
for ( ; i < 63; ++i)
putchar('-');
}
printf(" %8ld", jdprotocol ? c->joblen : c->bytelen);
putchar('\r');
}
#endif
/*
* This routine is used for reading headers.
* We don't expect to fill the buffer, so a simple scheme will do.
*/
void read_bytes(struct conn_info *c)
{
int count;
char *p;
reset_ptrs(c);
if ((count = sock_rbused(&c->sock)) > 0)
{
if (count > buffer_room(c))
count = buffer_room(c);
(void)sock_fastread(&c->sock, (byte *)c->bufip, count);
c->bufip += count;
}
if (queue_empty(c))
return;
nextstate:
switch (c->state)
{
case QUEUENAME:
queuename(c);
break;
case RECVJOB:
switch (*c->buffer)
{
case '\001':
ack_cmd(c);
c->state = CLOSING;
break;
case '\002':
c->state = CONTROLINFO;
c->controlfirst = 1;
goto nextstate;
case '\003':
c->state = DATAINFO;
goto nextstate;
}
case CONTROLINFO:
case DATAINFO:
p = c->bufip;
if (buffer_room(c) == 0)
--p;
*p = '\0';
if (sscanf(c->buffer+1, "%ld", &c->bytelen) != 1)
{
(void)printf("%s: %s, error parsing %s",
ptime(), lpt_names[c->printer], c->buffer+1);
c->state = NAKANDCLOSE;
return;
}
if (c->controlfirst && c->bytelen <= 0) /* 0 means EOF == EOJ */
c->bytelen = 0x7fffffff;
else
++c->bytelen; /* including EOF byte */
ack_cmd(c);
c->jobname[0] = '\0';
c->username[0] = '\0';
c->hostname[0] = '\0';
c->state = c->state == CONTROLINFO ? CONTROL : DATA;
if (c->state == DATA)
c->starttime = time(0);
reset_ptrs(c);
break;
case CONTROL:
/* copy out interesting information from buffer */
get_jobinfo(c);
c->bytelen -= count;
if (c->bytelen <= 0)
{
(void)printf("%s: Job ", ptime());
if (c->jobname[0] != '\0')
(void)printf("%s ", c->jobname);
if (c->username[0] != '\0' && c->hostname[0] != '\0')
(void)printf("for %s@%s ", c->username,
c->hostname);
(void)printf("on %s\n", lpt_names[c->printer]);
ack_cmd(c);
c->state = c->controlfirst ? DATAINFO : CLOSING;
}
break;
}
}
/*
* This routine is used for reading printable data.
* We read as much as the available space will allow us to.
* This may involve wraparound in buffer so the code is involved.
*/
void read_data(struct conn_info *c)
{
int count;
unsigned int n, ioff, toread;
char *p;
#ifdef MDEBUG
if (graphmem)
{
printf("> ");
trace_queue(c);
}
#endif
count = 0;
if ((n = queue_room(c)) > 0
&& tcp_tick(&c->sock)
&& (count = sock_rbused(&c->sock)) > 0)
{
if (count > n)
count = n; /* we need count again later */
else
n = count;
while (n > 0)
{
ioff = c->bufip - c->buffer; /* insertion point */
toread = buffer_size - ioff; /* at top of buffer? */
if (toread > n)
toread = n;
(void)sock_fastread(&c->sock, (byte *)c->bufip, toread);
ioff += toread;
n -= toread;
if (ioff >= buffer_size) /* wrap to bottom */
ioff = 0;
c->bufip = c->buffer + ioff;
}
c->bytelen -= count;
}
if (c->bytelen <= 0 && count > 0) /* back over EOF byte in lpd */
{
if (c->bufip == c->buffer)
c->bufip = c->buffer + buffer_size - 1;
else
--c->bufip;
}
/* yield to printer if nothing to read or time is up */
if (count <= 0 || --c->nturns <= 0)
{
c->state = PRINTING;
n = buffer_size / 8;
c->nturns = 8 - (queue_room(c) / n);
if (c->nturns <= 0)
c->nturns = 1;
}
}
/*
* Write buffer to console for debugging
*/
unsigned int console_outbuf(struct conn_info *c)
{
unsigned int ioff, ooff, printed;
ioff = c->bufip - c->buffer;
ooff = c->bufop - c->buffer;
printed = 0;
while (ooff != ioff)
{
putchar(*c->bufop++); /* write character */
if (++ooff >= buffer_size)
{
ooff = 0; /* wrap to bottom */
c->bufop = c->buffer;
}
++printed;
}
return (printed);
}
/*
* Direct hardware write to printer port
*/
unsigned int printer_outbuf(int port, struct conn_info *c)
{
int i, j, status;
unsigned int ioff, ooff, printed;
ioff = c->bufip - c->buffer;
ooff = c->bufop - c->buffer;
printed = 0;
inportb(port+1); /* read status */
status = (inportb(port+1) & 0xf8) ^ 0x48;
i = PRINT_CHUNK;
while (i-- > 0 && ooff != ioff && (status & P_READY) == P_READY)
{
outportb(port, *c->bufop++); /* write character */
if (++ooff >= buffer_size)
{
ooff = 0; /* wrap to bottom */
c->bufop = c->buffer;
}
outportb(port+2, 0x0d); /* raise strobe */
outportb(port+2, 0x0c); /* lower strobe */
++printed;
/* sample the busy line for a few tries */
for (j = 0; j < POLL_MAX; ++j)
{
status = (inportb(port+1) & 0xf8) ^ 0x48;
if ((status & P_READY) == P_READY)
break;
}
}
return (printed);
}
void print_data(struct conn_info *c)
{
int printer, current, delta, i;
unsigned int n, ioff, ooff;
struct lpt_info *p;
#ifdef MDEBUG
if (graphmem)
{
printf("< ");
trace_queue(c);
}
#endif
p = &lpt[printer = c->printer];
current = bios_printer_status(printer);
/* report changes from last time */
delta = (current ^ p->status) & P_CHANGES;
if (delta)
lpt_status_change(printer, delta, current);
p->status = current;
if (toconsole)
n = console_outbuf(c);
else if (!measureonly && p->hwaddr != 0)
n = printer_outbuf(p->hwaddr, c);
else
{
ioff = c->bufip - c->buffer;
ooff = c->bufop - c->buffer;
n = 0;
/* loop, printing a PRINT_CHUNK a time to prevent hogging */
i = PRINT_CHUNK;
/* eat your heart out, Pascal */
while (i-- > 0 && ooff != ioff &&
(measureonly ||
(bios_printer_status(printer) & P_READY) == P_READY))
{
if (!measureonly)
(void)bios_printer_outch(printer, *c->bufop);
++c->bufop;
/*
* The status returned by bios_printer_outch is useless because it
* gives the condition after printing and of course if there is no room
* or the printer is busy, it blocks
*/
++n;
if (++ooff >= buffer_size)
{
ooff = 0;
c->bufop = c->buffer;
}
}
}
c->joblen += n;
/* yield to reader if nothing printed or time is up */
if (n <= 0 || --c->nturns <= 0)
{
c->state = DATA;
n = buffer_size / 8;
c->nturns = queue_room(c) / n;
if (c->nturns <= 0)
c->nturns = 1;
}
if (c->bytelen <= 0 && queue_empty(c)) /* all printed? */
{
/* in jdprotocol bytelen will not be 0 before EOF
well, not before maxlong bytes anyway
so reach here only in lpd protocol */
ack_cmd(c);
c->state = c->controlfirst ? CLOSING : CONTROLINFO;
}
}
void show_stats(struct conn_info *c)
{
time_t elapsed;
if (c->printer < 0)
return;
elapsed = time(0) - c->starttime;
(void)printf("%s: %s: %ld bytes %ld seconds", ptime(),
lpt_names[c->printer], c->joblen, elapsed);
if (elapsed > 0)
(void)printf(" %ld bytes/second", c->joblen / elapsed);
(void)printf("\n");
}
void loop(int port, struct conn_info *c)
{
unsigned int cport;
struct sockaddr client;
int i;
char *name;
switch (c->state) {
case INIT:
if (jdprotocol)
{
if (port >= MAXLPT || lpt[port].avail != FREE)
break; /* don't offer connection */
cport = jdport + port;
c->printer = port;
}
else
cport = lpdport;
tcp_listen(&c->sock, cport, 0L, 0, NULL, 0);
(void)printf("%s: Connection %d listening on TCP port %u\n",
ptime(), port, cport);
c->state = WAITING;
c->nturns = MAXTURNS;
break;
case WAITING:
if (!tcp_tick(&c->sock))
{
c->state = CLOSING_NOSTATS;
break;
}
if (!sock_established(&c->sock))
break;
i = sizeof(client);
client.s_ip = 0;
name = getpeername(&c->sock, &client, &i) == 0 ?
inet_ntoa(c->buffer, client.s_ip) : "?";
if (check_access(client.s_ip))
{
(void)printf("%s: Connection %d from %s\n",
ptime(), port, name);
c->state = jdprotocol ? DATA : QUEUENAME;
c->bytelen = 0x7fffffff; /* maxlong */
c->starttime = time(0);
c->joblen = 0L;
}
else
{
(void)printf("%s: Connection %d from %s refused\n",
ptime(), port, name);
c->state = CLOSING_NOSTATS;
}
reset_ptrs(c);
break;
case QUEUENAME:
case RECVJOB:
case CONTROLINFO:
case CONTROL:
case DATAINFO:
if (tcp_tick(&c->sock))
read_bytes(c);
else
c->state = CLOSING; /* fatal */
break;
case DATA:
if (tcp_tick(&c->sock))
read_data(c);
else if (killjob) /* sender closed connection */
{
(void)printf("%s: Job aborted by sender\n", ptime());
c->state = ABORT;
}
/* don't shutdown until all printed */
else if (!queue_empty(c))
c->state = PRINTING;
else /* missing ack bug discovered by Steve Pacenka */
{
if (!jdprotocol)
ack_cmd(c);
c->state = CLOSING;
}
break;
case PRINTING:
if (tcp_tick(&c->sock))
print_data(c);
else if (killjob) /* sender closed connection */
{
(void)printf("%s: Job aborted by sender\n", ptime());
c->state = ABORT;
}
else if (!queue_empty(c))
print_data(c);
else /* missing ack bug discovered by Steve Pacenka */
{
if (!jdprotocol)
ack_cmd(c);
c->state = CLOSING;
}
break;
case NAKANDCLOSE:
nak_cmd(c);
case CLOSING:
show_stats(c);
case CLOSING_NOSTATS:
sock_flush(&c->sock);
sleep(1);
sock_close(&c->sock);
reinit:
/* free up printer */
if (0 <= c->printer && c->printer < MAXLPT
&& lpt[c->printer].avail == BUSY)
lpt[c->printer].avail = FREE;
c->state = INIT;
c->controlfirst = 0;
c->printer = -1;
break;
case ABORT:
sock_abort(&c->sock);
goto reinit;
}
}
void check_key(void)
{
int key, i;
if (((key = bioskey(0)) & 0xff) != 0)
return; /* ASCII key */
key = (key >> 8) & 0xff;
if (key < CF1 || key > CF3)
return;
key -= CF1; /* which printer? */
for (i = 0; i < MAXCON; ++i)
{
if (conn[i].printer == key && conn[i].state != INIT)
{
if (reinit)
{
(void)printf("%s: Aborting job and reinitialising %s\n",
ptime(), lpt_names[key]);
(void)biosprint(1, 0, key);
sleep(2); /* let printer settle */
}
else
{
(void)printf("%s: Aborting job on %s\n",
ptime(), lpt_names[key]);
}
conn[i].state = ABORT; /* abort job */
return;
}
}
}
void make_list(char *adjective, char *s,
longword table[], int *nentries, int maxtable)
{
char *p;
longword ip;
int i;
char buffer[64];
for ( ; *s != '\0'; s = p + 1)
{
if ((p = strchr(s, ',')) == 0)
p = s + strlen(s) - 1;
else
*p = '\0'; /* mark end */
if ((ip = resolve(s)) == (longword)0)
continue;
if (*nentries >= maxtable)
continue; /* should print warning */
table[(*nentries)++] = ip;
}
for (i = 0; i < *nentries; ++i)
(void)printf("%s ", inet_ntoa(buffer, table[i]));
if (*nentries > 0)
(void)printf("%s access\n", adjective);
}
void my_init(char *name, char *value)
{
if (strcmp(name, "PRINTER1NAME") == 0)
{
strncpy(lpt_names[0], value, sizeof(lpt_names[0]));
lpt_names[0][sizeof(lpt_names[0])-1] = '\0';
}
else if (strcmp(name, "PRINTER2NAME") == 0)
{
strncpy(lpt_names[1], value, sizeof(lpt_names[1]));
lpt_names[1][sizeof(lpt_names[1])-1] = '\0';
}
else if (strcmp(name, "PRINTER3NAME") == 0)
{
strncpy(lpt_names[2], value, sizeof(lpt_names[2]));
lpt_names[2][sizeof(lpt_names[2])-1] = '\0';
}
else if (normal_init)
(*normal_init)(name, value);
}
void options(int argc, char **argv)
{
int i;
char *s;
extern int optind;
extern char *optarg;
extern int getopt(int, char **, char *);
for (i = 0; i < MAXLPT; ++i)
lpt[i].hwaddr = 0;
while ((i = getopt(argc, argv, "123a:b:cd:gij:kl:mn:p:st")) > 0)
{
switch (i)
{
case '1': case '2': case '3':
lpt[i - '1'].avail = DISABLED;
break;
case 'a':
if (ndeny <= 0)
make_list("allowed", optarg, allow, &nallow, MAXALLOW);
break;
case 'b':
for (s = optarg; '1' <= *s && *s <= '3'; ++s)
lpt[*s - '1'].hwaddr = 1;
break;
case 'c':
toconsole = 1;
break;
case 'd':
if (nallow <= 0)
make_list("denied", optarg, deny, &ndeny, MAXDENY);
break;
case 'g':
graphmem = 1;
break;
case 'i':
reinit = 1;
break;
case 'j':
jdprotocol = 1;
if ((jdport = atoi(optarg)) <= 0)
jdport = JDPORT;
break;
case 'k':
killjob = 0;
break;
case 'l':
loghost = optarg;
break;
case 'm':
measureonly = 1;
break;
case 'n':
if ((nlpt = atoi(optarg)) > MAXLPT || nlpt < 0)
nlpt = 0;
break;
case 'p': /* alternate TCP port */
if ((lpdport = atoi(optarg)) <= 0)
lpdport = LPDPORT;
break;
case 's':
check_subnet = 0;
break;
case 't':
notone = 1;
break;
}
}
}
int main(int argc, char **argv)
{
int i;
(void)printf(PROGRAM " " VERSION " " AUTHOR "\n");
(void)printf(PROGRAM " comes with ABSOLUTELY NO WARRANTY.\n");
(void)printf("This is Postcardware, and you are welcome to redistribute it\n");
(void)printf(" under certain conditions; see the file Artistic for details.\n");
(void)printf("C-F1 through C-F3 to abort printer jobs\n");
(void)memset(conn, 0, sizeof(conn));
(void)memset(lpt, 0, sizeof(lpt));
tzset();
options(argc, argv);
/* hook onto init procedure to get NAME=VALUE pairs */
normal_init = usr_init;
usr_init = my_init;
dbuginit();
sock_init();
init_queues();
if (nlpt == 0)
{
(void)printf("%s: No printers connected\n", ptime());
return (1);
}
init_log();
for (;;)
{
for (i = 0; i < MAXCON; ++i)
loop(i, &conn[i]);
if (kbhit())
check_key();
}
/*NOTREACHED*/
return (0);
}